/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>


struct node_fs root = {
	.type = TYPE_FS,
	.name = "",
	NODE_FS_INIT_INIT,
	NODE_FS_OPS_INIT,
	NODE_FS_INIT_VARS,
	NODE_FS_LIST_INIT
};

int fs_enumerate(struct node_fs *n)
{
	struct node_fs *t;
	if(n->flags & ENUMERATED)
		return 0;
	n->enumerate(n);
	if(n->flags & HAS_ENUMERATORS) {
		list_for((n->links), t) {
			if(t->type & TYPE_FS_ENUM)
				t->enumerate(t);
		}
	}
	n->flags |= ENUMERATED;
	return 0;
}


static struct node_fs *find_link (struct node_fs *n, const char *name)
{
	struct node_fs *t;
	fs_enumerate(n);
	for(t = n->links; t; t = t->next) {
		if(t->type & TYPE_FSMASK) {
			if(!strcmp(t->name,name))
				return t;
		} else {
			arch_delay_ms(100);
			printk("Skipped finding %s\n", t->name);
		}
	}
	return NULL;
}

static char path[MAX_FS_NAME_LEN];
static struct node_fs *parse_path_name(const char *s)
{
	struct node_fs *n = &root;
	char num[10];
	u32 d=0;
	if(s[0]=='/')
		d++;
	while(1) {
		getstringparameter(s, path, '/', d);
		if(!strlen(path))
			break;
		if(!strcmp(path,".")) {} 
		
		else if(!strcmp(path,".."))
			n = n->parent;
		else
			n = find_link (n, path);
		if(!n)
			return NULL;
		d++;
	}
	return n;
}

static struct node_fs *parse_path_name_relative(struct node_fs *n, const char *s)
{
	char num[10];
	u32 d=0;
	if(s[0]=='/') {
		n = &root;
		d++;
	}
	while(1) {
		getstringparameter(s, path, '/', d);
		if(!strlen(path))
			break;
		if(!strcmp(path,".")) {} 
		
		else if(!strcmp(path,".."))
			n = n->parent;
		else
			n = find_link (n, path);
		if(!n)
			return NULL;
		d++;
	}
	return n;
}



int fs_link(const char *path,struct node_fs *link)
{
	struct node_fs *in, *t;
	
	in = parse_path_name(path);
	
	if(!in)
		return ENOREG;
	//Check if the node is already plugged in. In order for this check to fail, you need to use NODE_FS_LIST_INIT for each declared node.
	if(list_present(in->links, link)) {
		return EPINV;
	}
	
	list_attach(in->links, in->linkstail, link);
	
	link->parent = in;
	
	if(!(in->type & TYPE_FS_NOTIFY) && !(in->type & TYPE_FS_ENUM)) { //Check for special fs nodes
		in->notify(in, link);
		if(in->flags & HAS_NOTIFIERS) {
			list_for((in->links), t) {
				if(t->type & TYPE_FS_NOTIFY)
					t->notify(t, link);
			}
		}
	}
	return 0;
}

int fs_noparse_link(struct node_fs *in, struct node_fs *link)
{
	struct node_fs *t;
	
	if(list_present(in->links, link)) {
		return EPINV;
	}

	list_attach(in->links, in->linkstail, link);
	
	link->parent = in;
	
	if(!(link->type & TYPE_FS_NOTIFY) && !(link->type & TYPE_FS_ENUM)) { //Check for special fs nodes
		in->notify(in, link);
		if(in->flags & HAS_NOTIFIERS) {
			list_for((in->links), t) {
				if(t->type & TYPE_FS_NOTIFY)
					t->notify(t, link);
			}
		}
	}
	return 0;
}

int fs_read(const char *path, void *buf, u32 offset, u32 count)
{
	struct node_fs *in, *t;
	u32 total_count;
	in = parse_path_name(path);
	if(!in)
		return ENOREG;
	return in->read(in, buf, offset, count);
}

int fs_write(const char *path, void *buf, u32 offset, u32 count)
{
	struct node_fs *in;
	in = parse_path_name(path);
	if(!in)
		return ENOREG;
	return in->write(in, buf, offset, count);
}	

int fs_unlink(const char *path,struct node_fs *link)
{
	struct node_fs *in;
	in = parse_path_name(path);
	if(!in)
		return ENOREG;
	list_detach(in->links, in->linkstail,link);
	return 0;
}

int fs_noparse_unlink(struct node_fs *node, struct node_fs *link)
{
	list_detach(node->links, node->linkstail, link);
	return 0;
}

//node api


struct node_fs *fs_get(const char *path)
{
	return parse_path_name(path);
}

struct node_fs *fs_get_relative(struct node_fs *n, const char *path)
{
	return parse_path_name_relative(n, path);
}


static char *generate_path_name(char *s, struct node_fs *node)
{
	if(node) {
		strcat(generate_path_name(s, node->parent), node->name);
		strcat(s,"/");
		return s;
	} else
		return s;
}

char *fs_path(char *s, struct node_fs *node)
{
	return generate_path_name(s, node);
}

static void tree(struct node_fs *node)
{
	struct node_fs *t;
	char tree_path[MAX_FS_NAME_LEN];
	for(t = node->links; t; t = t->next) {
		
		memset(tree_path, 0, MAX_FS_NAME_LEN);
		printk("%s\n", generate_path_name(tree_path, t));
		
		if(!(t->type & TYPE_FS_NOTIFY) && !(t->type & TYPE_FS_ENUM)) {
			fs_enumerate(t);
			tree(t);
		}
	}
}

int fs_tree(char *s)
{
	struct node_fs *t;
	t = fs_get(s);
	if(!t) {
		printk("fs_tree: %s not found\n", s);
		return ENOREG;
	}
	fs_enumerate(t);
	tree(t);
	return 0;
}


struct node_fs *fs_create(char *name)
{
	struct node_fs *n;
	n = (struct node_fs *)malloc(sizeof(struct node_fs));
	if(!n)
		return NULL;
	INIT_NODE_FS_LIST(n);
	INIT_NODE_FS_OPS(n);
	INIT_NODE_FS_INIT(n);
	n->class = NULL;
	INIT_NODE_FS_VARS(n);
	strcpy(n->name, name);
	n->type = TYPE_FS;
	return n;
}

int fs_move(char *to, char *from)
{
	struct node_fs *to_fs, *from_fs;
	to_fs = fs_get(to);
	from_fs = fs_get(from);
	fs_noparse_unlink(from_fs->parent, from_fs);
	fs_noparse_link(to_fs, from_fs);
	from_fs->parent = to_fs;
	return 0;
}

int fs_noparse_move(struct  node_fs *to_fs, struct node_fs *from_fs)
{
	fs_noparse_unlink(from_fs->parent, from_fs);
	fs_noparse_link(to_fs, from_fs);
	from_fs->parent = to_fs;
	return 0;
}


//Register as an enumerator on n
int fs_noparse_register_enumerator(struct node_fs *n, struct node_fs *x)
{
	x->type |= TYPE_FS_ENUM;
	n->flags |= HAS_ENUMERATORS;
	fs_noparse_link(n, x);
	return 0;
}


//Register to be notified by n upon child addition
int fs_noparse_register_notifier(struct node_fs *n, struct node_fs *x)
{
	x->type |= TYPE_FS_NOTIFY;
	n->flags |= HAS_NOTIFIERS;
	fs_noparse_link(n, x);
	return 0;
}


int fs_register_enumerator(const char *path, struct node_fs *x)
{
	struct node_fs *n;
	n = fs_get(path);
	if(!n)
		return ENOREG;
	return fs_noparse_register_enumerator(n, x);
}

int fs_register_notifier(const char *path, struct node_fs *x)
{
	struct node_fs *n;
	n = fs_get(path);
	if(!n)
		return ENOREG;
	return fs_noparse_register_notifier(n, x);
}



struct node_fs *fs_new_notifier(const char *path, int (*notify)(struct node_fs *this, struct node_fs *link), char *name)
{
	struct node_fs *x;
	x = fs_create(name);
	x->type = TYPE_FS_CLIENT;
	x->notify = notify;
	fs_register_notifier(path, x);
	return x;
}

struct node_fs *fs_new_enumerator(const char *path, int (*enumerate)(struct node_fs *this), char *name)
{
	struct node_fs *x;
	x = fs_create(name);
	x->type = TYPE_FS_SERVER;
	x->enumerate = enumerate;
	fs_register_enumerator(path, x);
	return x;
}

struct node_fs *fs_noparse_new_notifier(struct node_fs *n, int (*notify)(struct node_fs *this, struct node_fs *link), char *name)
{
	struct node_fs *x;
	x = fs_create(name);
	x->type = TYPE_FS_CLIENT;
	x->notify = notify;
	fs_noparse_register_notifier(n, x);
	return x;
}

struct node_fs *fs_noparse_new_enumerator(struct node_fs *n, int (*enumerate)(struct node_fs *this), char *name)
{
	struct node_fs *x;
	x = fs_create(name);
	x->type = TYPE_FS_SERVER;
	x->enumerate = enumerate;
	fs_noparse_register_enumerator(n, x);
	return x;
}


//Fs iterator



int fs_iterator_parse(struct node_fs **fs, const char *path)
{
	*fs = fs_get(path);
	return 0;
}

int fs_iterator_get_child(struct node_fs **fs)
{
	*fs = (*fs)->links;
	while((*fs) && (((*fs)->type & TYPE_FS_NOTIFY) || ((*fs)->type & TYPE_FS_ENUM)))
		*fs = (*fs)->next;
	return 0;
}

int fs_iterator_get_next(struct node_fs **fs)
{
	*fs = (*fs)->next;
	while((*fs) && (((*fs)->type & TYPE_FS_NOTIFY) || ((*fs)->type & TYPE_FS_ENUM)))
		*fs = (*fs)->next;
	return 0;
}



int init_fs()
{
	return 0;
}

void fs()
{
	tree(&root);
}
